﻿using System;
using System.Collections.Generic;
using System.IO;
using OpenXml.Internal;
using OpenXml.Internal.IO.Packaging;
using OpenXml.Internal.IO.Zip;
using OpenXml.IO.Packaging;
using OpenXml.Utility;

namespace OpenXml.IO.CTProviders
{
    public abstract class ContentTypeProvider
    {
        private static readonly int DEFAULT_DICTIONARY_INITIAL_SIZE = 0x10;
        private static readonly string DEFAULT_CONTENT_TYPES_FILE = "[Content_Types].xml";

        private bool _dirty;
        private bool _contentTypeStreamExists;
        private bool _isInitialized;

        //TODO: возможно следует сделать private ExtensionEqualityComparer
        protected static readonly ExtensionEqualityComparer ExtensionEqualityComparer = new ExtensionEqualityComparer();
        protected ZipArchive ZipArchive;
        protected ZipFileInfo ContentTypeFileInfo;
        protected List<PieceInfo> ContentTypeStreamPieces;

        private Dictionary<String, ContentType> _defaultDictionary;
        protected Dictionary<String, ContentType> DefaultDictionary
        {
            get
            {
                return this._defaultDictionary ?? (this._defaultDictionary = new Dictionary<String, ContentType>());
            }
            set
            {
                this._defaultDictionary = value;
            }
        }

        private Dictionary<PackUriHelper.ValidatedPartUri, ContentType> _overrideDictionary;
        protected Dictionary<PackUriHelper.ValidatedPartUri, ContentType> OverrideDictionary
        {
            get {
                return this._overrideDictionary ??
                       (this._overrideDictionary = new Dictionary<PackUriHelper.ValidatedPartUri, ContentType>());
            }
            set
            {
                this._overrideDictionary = value;
            }
        }

        public abstract string ContentTypeFileName { get; }

        protected ContentTypeProvider()
        {
            this._isInitialized = false;
        }

        protected ContentTypeProvider(ZipArchive zipArchive)
        {
            this._isInitialized = true;
            this.ZipArchive = zipArchive;
            this.DefaultDictionary = new Dictionary<string, ContentType>(DEFAULT_DICTIONARY_INITIAL_SIZE, ExtensionEqualityComparer);
            if ((this.ZipArchive.OpenAccess == FileAccess.Read) || (this.ZipArchive.OpenAccess == FileAccess.ReadWrite))
            {
                this.ParseContentTypesFile(this.ZipArchive.GetFiles());
            }
            this._dirty = false;
        }

        private String GetContentTypeName()
        {
            return String.IsNullOrEmpty(ContentTypeFileName) ? DEFAULT_CONTENT_TYPES_FILE : ContentTypeFileName;
        }

        public abstract ContentTypeProvider Create(ZipArchive zipArchive);

        protected abstract void ParseContentType(Stream ctStream);
        private void ParseContentTypesFile(ZipFileInfoCollection zipFiles)
        {
            if (!this._isInitialized)
                throw new NullReferenceException(SR.Get("ContentTypeProviderCannotInizialized"));

            Stream input = this.OpenContentTypeStream(zipFiles);
            if (input != null)
            {
                ParseContentType(input);
            }
        }

        private Stream OpenContentTypeStream(ZipFileInfoCollection zipFiles)
        {
            SortedDictionary<PieceInfo, ZipFileInfo> dictionary = null;
            foreach (ZipFileInfo info in zipFiles)
            {
                if (info.Name.ToUpperInvariant().StartsWith(GetContentTypeName().ToUpper(), StringComparison.Ordinal))
                {
                    if (info.Name.Length == GetContentTypeName().Length)
                    {
                        this.ContentTypeFileInfo = info;
                    }
                    else
                    {
                        PieceInfo info2;
                        if (PieceNameHelper.TryCreatePieceInfo(info, out info2))
                        {
                            if (dictionary == null)
                            {
                                dictionary = new SortedDictionary<PieceInfo, ZipFileInfo>(PieceNameHelper.PieceNameComparer);
                            }
                            dictionary.Add(info2, info);
                        }
                    }
                }
            }
            List<PieceInfo> pieces = null;
            if (dictionary != null)
            {
                pieces = new List<PieceInfo>(dictionary.Keys);
                if (pieces[0].PieceNumber != 0)
                {
                    pieces = null;
                }
                else
                {
                    int num = -1;
                    for (int i = 0; i < pieces.Count; i++)
                    {
                        if (pieces[i].PieceNumber != i)
                        {
                            pieces = null;
                            break;
                        }
                        if (pieces[i].IsLastPiece)
                        {
                            num = i;
                            break;
                        }
                    }
                    if (pieces != null)
                    {
                        if (num == -1)
                        {
                            pieces = null;
                        }
                        else if (num < (pieces.Count - 1))
                        {
                            pieces.RemoveRange(num + 1, (pieces.Count - num) - 1);
                        }
                    }
                }
            }
            if ((this.ContentTypeFileInfo != null) && (pieces != null))
            {
                throw new FormatException(SR.Get("BadPackageFormat"));
            }
            if (this.ContentTypeFileInfo != null)
            {
                this._contentTypeStreamExists = true;
                return this.ContentTypeFileInfo.GetStream(FileMode.Open, FileAccess.Read);
            }
            if (pieces != null)
            {
                this._contentTypeStreamExists = true;
                this.ContentTypeStreamPieces = pieces;
                return new InterleavedZipPartStream(pieces[0].PrefixName, pieces, FileMode.Open, FileAccess.Read);
            }
            return null;
        }

        internal ContentType GetContentType(PackUriHelper.ValidatedPartUri partUri)
        {
            if ((this._overrideDictionary != null) && this._overrideDictionary.ContainsKey(partUri))
            {
                return this._overrideDictionary[partUri];
            }
            string partUriExtension = partUri.PartUriExtension;
            if (this._defaultDictionary.ContainsKey(partUriExtension))
            {
                return this._defaultDictionary[partUriExtension];
            }
            return null;
        }
    }

    public class ExtensionEqualityComparer : IEqualityComparer<String>
    {
        bool IEqualityComparer<string>.Equals(string extensionA, string extensionB)
        {
            Invariant.Assert(extensionA != null, "extenstion should not be null");
            Invariant.Assert(extensionB != null, "extenstion should not be null");
            if (extensionA != null && extensionB != null)
                    return (string.CompareOrdinal(extensionA.ToUpperInvariant(), extensionB.ToUpperInvariant()) == 0);
            return false;
        }

        int IEqualityComparer<string>.GetHashCode(string extension)
        {
            Invariant.Assert(extension != null, "extenstion should not be null");
            if (extension != null) return extension.ToUpperInvariant().GetHashCode();
            return 0;
        }
    }
}


